#include "gd32f30x.h"
#include "stdint.h"
#include "stdio.h"
#include "string.h"
#include "DevicesFlash.h"
#include "DevicesSPIFlash.h"
#include "DevicesCRC.h"
#include "DriverBootloader.h"


/* 各固件参数 */
static FirmwareInfoType st_typeFirmwareInfo;

#define FIRMWARE_BUFF_LENGTH 256
/* 从Flash读写数据用的缓存 */
uint8_t st_ucFirmwareBuff[FIRMWARE_BUFF_LENGTH] = {0};


/* 一些出厂的固定参数 */
void vFirmwareFactoryInit(void)
{
    /* 固定参数 */
    st_typeFirmwareInfo.bootloader.address = FLASH_BOOTLOADER_ADDR;
    st_typeFirmwareInfo.bootloader.registers &= ~FIRMWARE_SOURCE_SPI_FLASH;

    st_typeFirmwareInfo.bootloaderBackInside.address = FLASH_BOOTLOADER_BACK_ADDR;
    st_typeFirmwareInfo.bootloaderBackInside.registers &= ~FIRMWARE_SOURCE_SPI_FLASH;

    st_typeFirmwareInfo.bootloaderBackOutside.address = SPI_FLASH_BOOTLOADER_BACK_ADDR;
    st_typeFirmwareInfo.bootloaderBackOutside.registers |= FIRMWARE_SOURCE_SPI_FLASH;

    /* 固定参数 */
    st_typeFirmwareInfo.app.address = FLASH_APP_ADDR;
    st_typeFirmwareInfo.app.registers &= ~FIRMWARE_SOURCE_SPI_FLASH;

    st_typeFirmwareInfo.appBackInside.address = SPI_FLASH_APP_BACK_ADDR;
    st_typeFirmwareInfo.appBackInside.registers &= ~FIRMWARE_SOURCE_SPI_FLASH;

    st_typeFirmwareInfo.appBackOutside.address = SPI_FLASH_APP_BACK_ADDR;
    st_typeFirmwareInfo.appBackOutside.registers |= FIRMWARE_SOURCE_SPI_FLASH;
}

/* 从Flash读取参数，并初始化 */
void vFirmwareInit(void)
{
    cFlashReadDatas(FLASH_SYSTEM_DATA_ADDR, (uint8_t *)(&st_typeFirmwareInfo), sizeof(st_typeFirmwareInfo));

    vFirmwareFactoryInit();
}

/* 把参数写入到Flash */
int8_t cFirmwareWrite(void)
{
    int8_t cError = 0;

    vFirmwareFactoryInit();

    cError = cFlashWriteDatas(FLASH_SYSTEM_DATA_ADDR, (uint8_t *)(&st_typeFirmwareInfo), sizeof(st_typeFirmwareInfo));

    return cError;
}

FirmwareInfoType *ptypeFirmwareInfoGet(void)
{
    return &st_typeFirmwareInfo;
}

void vFirmwareInfoRegistersSet(uint32_t uiChannel, uint32_t uiRegisters)
{
    switch(uiChannel)
    {
        case FIRMWARE_CHANNEL_BOOTLOADER                : st_typeFirmwareInfo.bootloader.registers              = uiRegisters; break;
        case FIRMWARE_CHANNEL_BOOTLOADER_BACK_INSIDE    : st_typeFirmwareInfo.bootloaderBackInside.registers    = uiRegisters; break;
        case FIRMWARE_CHANNEL_BOOTLOADER_BACK_OUTSIDE   : st_typeFirmwareInfo.bootloaderBackOutside.registers   = uiRegisters; break;
        case FIRMWARE_CHANNEL_APP                       : st_typeFirmwareInfo.app.registers                     = uiRegisters; break;
        case FIRMWARE_CHANNEL_APP_BACK_INSIDE           : st_typeFirmwareInfo.appBackInside.registers           = uiRegisters; break;
        case FIRMWARE_CHANNEL_APP_BACK_OUTSIDE          : st_typeFirmwareInfo.appBackOutside.registers          = uiRegisters; break;

        default : break;
    }
}

uint32_t uiFirmwareCRCUpdate(FirmwarePortInfoType *pHandle)
{
    uint32_t uiAdddrNow = 0;
    int32_t iLength = 0, iLengthNow = 0;
    uint16_t usCRCValue = 0xFFFF;
    int8_t cError = 0;

    if(pHandle == NULL)
        return 1;

    if(pHandle->length > 1024 * 1024)
        return 2;

    uiAdddrNow = pHandle->address;
    iLength = pHandle->length;

    /* CRC计算 */
    while(iLength > 0)
    {
        iLengthNow = (iLength > FIRMWARE_BUFF_LENGTH) ? FIRMWARE_BUFF_LENGTH : iLength;

        if((pHandle->registers & FIRMWARE_SOURCE_SPI_FLASH) != FIRMWARE_SOURCE_SPI_FLASH)
            cError |= cFlashReadDatas(uiAdddrNow, st_ucFirmwareBuff, iLengthNow);
        else
            cError |= cSPIFlashReadDatas(uiAdddrNow, st_ucFirmwareBuff, iLengthNow);

        if(cError != 0)
        {
            break;
        }

        usCRCValue = usCRC16_MODBUS(&usCRCValue, st_ucFirmwareBuff, iLengthNow);

        uiAdddrNow += iLengthNow;
        iLength -= iLengthNow;
    }

    return usCRCValue;
}

int8_t cFirmwareUpdate(FirmwarePortInfoType *pTarget, FirmwarePortInfoType *pSource)
{
    uint32_t uiSourceAdddrNow = 0, uiTargetAdddrNow = 0, uiCRCValue = 0;
    int32_t iLength = 0, iLengthNow = 0;
    int8_t cError = 0;

    if((pSource == NULL) || (pTarget == NULL))
        return 1;

    /* CRC校验，判断源固件是否完整 */
    if((uiCRCValue = uiFirmwareCRCUpdate(pSource)) != pSource->crcValue)
    {
        printf("Source CRC:%08X CRC:%08X error.\r\n", uiCRCValue, pSource->crcValue);
        return 2;
    }

    /* CRC比较，判断是否是一样的固件，如果是一样的固件，就直接退出表示成功 */
    if(uiFirmwareCRCUpdate(pTarget) == pSource->crcValue)
        return 0;

    uiSourceAdddrNow = pSource->address;
    uiTargetAdddrNow = pTarget->address;
    iLength = pSource->length;

    printf("update file size: %d\r\n", (int)(pSource->length));

    /* 升级覆盖 */
    while(iLength > 0)
    {
        iLengthNow = (iLength > FIRMWARE_BUFF_LENGTH) ? FIRMWARE_BUFF_LENGTH : iLength;

        if((pSource->registers & FIRMWARE_SOURCE_SPI_FLASH) != FIRMWARE_SOURCE_SPI_FLASH)
            cError |= cFlashReadDatas(uiSourceAdddrNow, st_ucFirmwareBuff, iLengthNow);
        else
            cError |= cSPIFlashReadDatas(uiSourceAdddrNow, st_ucFirmwareBuff, iLengthNow);

        cError |= cFlashWriteDatas(uiTargetAdddrNow, st_ucFirmwareBuff, iLengthNow);

        if(cError != 0)
        {
            return 4;
        }

        uiSourceAdddrNow += iLengthNow;
        uiTargetAdddrNow += iLengthNow;
        iLength -= iLengthNow;

        printf("update: %d%%\r\n", (int)(100 * iLength / pSource->length));
    }

    /* CRC比较，以判断是否已经更新成功 */
    pTarget->length = pSource->length;
    if(uiFirmwareCRCUpdate(pTarget) != pSource->crcValue)
    {
        printf("固件拷贝后，校验目标区域新固件CRC失败.\r\n");
        return 5;
    }

    return cError;
}

/* 跳转到指定位置执行 */
int8_t cFirmwareJUMPTo(uint32_t uiAddress)
{
    typedef void (*pFunction)(void);
    uint32_t uiJumpAddress = 0, uiHeapData = 0;
    pFunction typeJumpToAPPlication = 0;

    uiHeapData = *(__IO uint32_t*)uiAddress;
    /* Test if user code is programmed starting from address "ApplicationAddress" */
    if (((uiHeapData) & 0x200FFFFF ) == uiHeapData)
    {
        /* Jump to application */
        uiJumpAddress = *(__IO uint32_t*) (uiAddress + 4);
        typeJumpToAPPlication = (pFunction) uiJumpAddress;

        /* Initialize application's Stack Pointer */
        __set_MSP(*(__IO uint32_t*) uiAddress);

        /* 跳转到APP程序 */
        typeJumpToAPPlication();
    }

    return 1;
}

int8_t cFirmwareJump(FirmwarePortInfoType *pHandle)
{
    /* CRC校验，判断源固件是否完整 */
    if((pHandle->crcValue == FIRMWARE_UNLOCK_CRC) || (uiFirmwareCRCUpdate(pHandle) == pHandle->crcValue))
    {
        /* 程序跳转到目标地址执行 */
        cFirmwareJUMPTo(pHandle->address);
    }

    return 1;
}

int8_t cFirmwareBootloaderJump(void)
{
    if(((st_typeFirmwareInfo.bootloaderBackInside.registers & FIRMWARE_UPDATE) == 0) &&
      ( (st_typeFirmwareInfo.bootloaderBackOutside.registers & FIRMWARE_UPDATE) == 0))
    {
        /* 进行程序跳转 */
        cFirmwareJump(&st_typeFirmwareInfo.bootloader);

        /* 程序跳转失败，置位更新标志，以提示后续更新 */
        vFirmwareInfoRegistersSet(FIRMWARE_CHANNEL_BOOTLOADER_BACK_INSIDE, FIRMWARE_UPDATE | FIRMWARE_SOURCE_ROM);
        vFirmwareInfoRegistersSet(FIRMWARE_CHANNEL_BOOTLOADER_BACK_OUTSIDE, FIRMWARE_UPDATE | FIRMWARE_SOURCE_SPI_FLASH);
    }

    return 1;
}

int8_t cFirmwareAPPJump(void)
{
    if(((st_typeFirmwareInfo.appBackInside.registers & FIRMWARE_UPDATE) == 0) &&
       ((st_typeFirmwareInfo.appBackOutside.registers & FIRMWARE_UPDATE) == 0))
    {
        /* 进行程序跳转 */
        cFirmwareJump(&st_typeFirmwareInfo.app);

        /* 程序跳转失败，置位更新标志，以提示后续更新 */
        vFirmwareInfoRegistersSet(FIRMWARE_CHANNEL_APP_BACK_INSIDE, FIRMWARE_UPDATE | FIRMWARE_SOURCE_ROM);
        vFirmwareInfoRegistersSet(FIRMWARE_CHANNEL_APP_BACK_OUTSIDE, FIRMWARE_UPDATE | FIRMWARE_SOURCE_SPI_FLASH);
    }

    return 1;
}

int8_t cFirmwareBootloaderUpdate(void)
{
    int8_t cError = 0;

    /* 需要更新 bootloader */
    if((st_typeFirmwareInfo.bootloaderBackInside.registers & FIRMWARE_UPDATE) != 0)
    {
        if(cFirmwareUpdate(&st_typeFirmwareInfo.bootloader, &st_typeFirmwareInfo.bootloaderBackInside) == 0)
        {
            /* 更新目标固件参数（版本号） */
            st_typeFirmwareInfo.bootloader = st_typeFirmwareInfo.bootloaderBackInside;

            printf("\n\rbootloader from inside 更新成功.\r\n");
        }
        else
        {
            printf("\n\rbootloader from inside 更新失败.\r\n");
            cError = 1;
        }

        vFirmwareInfoRegistersSet(FIRMWARE_CHANNEL_BOOTLOADER_BACK_INSIDE, FIRMWARE_SOURCE_ROM);
    }

    if((st_typeFirmwareInfo.bootloaderBackOutside.registers & FIRMWARE_UPDATE) != 0)
    {
        if(cFirmwareUpdate(&st_typeFirmwareInfo.bootloader, &st_typeFirmwareInfo.bootloaderBackOutside) == 0)
        {
            /* 更新目标固件参数（版本号） */
            st_typeFirmwareInfo.bootloader = st_typeFirmwareInfo.bootloaderBackOutside;

            printf("\n\rbootloader from outside 更新成功.\r\n");
        }
        else
        {
            printf("\n\rbootloader from outside  更新失败.\r\n");
            cError = 1;
        }

        vFirmwareInfoRegistersSet(FIRMWARE_CHANNEL_BOOTLOADER_BACK_OUTSIDE, FIRMWARE_SOURCE_SPI_FLASH);
    }


    return cError;
}

int8_t cFirmwareAPPUpdate(void)
{
    int8_t cError = 0;

    /* 需要更新 app */
    if((st_typeFirmwareInfo.appBackInside.registers & FIRMWARE_UPDATE) != 0)
    {
        if(cFirmwareUpdate(&st_typeFirmwareInfo.app, &st_typeFirmwareInfo.appBackInside) == 0)
        {
            /* 更新目标固件参数 */
            st_typeFirmwareInfo.app = st_typeFirmwareInfo.appBackInside;

            printf("\n\rapp from inside 更新成功.\r\n");
        }
        else
        {
            printf("\n\rapp from inside 更新失败.\r\n");
            cError = 1;
        }

        vFirmwareInfoRegistersSet(FIRMWARE_CHANNEL_APP_BACK_INSIDE, FIRMWARE_SOURCE_ROM);
    }

    if((st_typeFirmwareInfo.appBackOutside.registers & FIRMWARE_UPDATE) != 0)
    {
        if(cFirmwareUpdate(&st_typeFirmwareInfo.app, &st_typeFirmwareInfo.appBackOutside) == 0)
        {
            /* 更新目标固件参数 */
            st_typeFirmwareInfo.app = st_typeFirmwareInfo.appBackOutside;

            printf("\n\rapp from outside 更新成功.\r\n");
        }
        else
        {
            printf("\n\rapp from outside  更新失败.\r\n");
            cError = 1;
        }

        vFirmwareInfoRegistersSet(FIRMWARE_CHANNEL_APP_BACK_OUTSIDE, FIRMWARE_SOURCE_SPI_FLASH);
    }

    return cError;
}
